/* * Autopsy Forensic Browser * * Copyright 2013-16 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.imagegallery.gui; import com.google.common.collect.Lists; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.DoubleProperty; import javafx.collections.FXCollections; import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.Slider; import javafx.scene.control.SplitMenuButton; import javafx.scene.control.ToolBar; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.text.Text; import org.controlsfx.control.PopOver; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.FXMLConstructor; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.actions.CategorizeGroupAction; import org.sleuthkit.autopsy.imagegallery.actions.TagGroupAction; import org.sleuthkit.autopsy.imagegallery.datamodel.Category; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState; import org.sleuthkit.datamodel.TskCoreException; /** * Controller for the ToolBar */ public class Toolbar extends ToolBar { private static final Logger LOGGER = Logger.getLogger(Toolbar.class.getName()); private static final int SIZE_SLIDER_DEFAULT = 100; @FXML private ImageView sortHelpImageView; @FXML private ComboBox<DrawableAttribute<?>> groupByBox; @FXML private Slider sizeSlider; @FXML private SplitMenuButton catGroupMenuButton; @FXML private SplitMenuButton tagGroupMenuButton; @FXML private Label groupByLabel; @FXML private Label tagImageViewLabel; @FXML private Label categoryImageViewLabel; @FXML private Label thumbnailSizeLabel; private final ImageGalleryController controller; private SortChooser<DrawableGroup, GroupSortBy> sortChooser; private final InvalidationListener queryInvalidationListener = new InvalidationListener() { public void invalidated(Observable o) { controller.getGroupManager().regroup( groupByBox.getSelectionModel().getSelectedItem(), sortChooser.getComparator(), sortChooser.getSortOrder(), false); } }; public DoubleProperty thumbnailSizeProperty() { return sizeSlider.valueProperty(); } @FXML @NbBundle.Messages({"Toolbar.groupByLabel=Group By:", "Toolbar.sortByLabel=Sort By:", "Toolbar.ascRadio=Ascending", "Toolbar.descRadio=Descending", "Toolbar.tagImageViewLabel=Tag Group's Files:", "Toolbar.categoryImageViewLabel=Categorize Group's Files:", "Toolbar.thumbnailSizeLabel=Thumbnail Size (px):", "Toolbar.sortHelp=The sort direction (ascending/descending) affects the queue of unseen groups that Image Gallery maintains, but changes to this queue aren't apparent until the \"Next Unseen Group\" button is pressed.", "Toolbar.sortHelpTitle=Group Sorting",}) void initialize() { assert catGroupMenuButton != null : "fx:id=\"catSelectedMenubutton\" was not injected: check your FXML file 'Toolbar.fxml'."; assert groupByBox != null : "fx:id=\"groupByBox\" was not injected: check your FXML file 'Toolbar.fxml'."; assert sizeSlider != null : "fx:id=\"sizeSlider\" was not injected: check your FXML file 'Toolbar.fxml'."; assert tagGroupMenuButton != null : "fx:id=\"tagSelectedMenubutton\" was not injected: check your FXML file 'Toolbar.fxml'."; controller.viewState().addListener((observable, oldViewState, newViewState) -> { Platform.runLater(() -> syncGroupControlsEnabledState(newViewState)); }); syncGroupControlsEnabledState(controller.viewState().get()); try { TagGroupAction followUpGroupAction = new TagGroupAction(controller.getTagsManager().getFollowUpTagName(), controller); tagGroupMenuButton.setOnAction(followUpGroupAction); tagGroupMenuButton.setText(followUpGroupAction.getText()); tagGroupMenuButton.setGraphic(followUpGroupAction.getGraphic()); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Could create follow up tag menu item", ex); //NON-NLS } tagGroupMenuButton.showingProperty().addListener(showing -> { if (tagGroupMenuButton.isShowing()) { List<MenuItem> selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(), tn -> GuiUtils.createAutoAssigningMenuItem(tagGroupMenuButton, new TagGroupAction(tn, controller))); tagGroupMenuButton.getItems().setAll(selTagMenues); } }); CategorizeGroupAction cat5GroupAction = new CategorizeGroupAction(Category.FIVE, controller); catGroupMenuButton.setOnAction(cat5GroupAction); catGroupMenuButton.setText(cat5GroupAction.getText()); catGroupMenuButton.setGraphic(cat5GroupAction.getGraphic()); catGroupMenuButton.showingProperty().addListener(showing -> { if (catGroupMenuButton.isShowing()) { List<MenuItem> categoryMenues = Lists.transform(Arrays.asList(Category.values()), cat -> GuiUtils.createAutoAssigningMenuItem(catGroupMenuButton, new CategorizeGroupAction(cat, controller))); catGroupMenuButton.getItems().setAll(categoryMenues); } }); groupByLabel.setText(Bundle.Toolbar_groupByLabel()); tagImageViewLabel.setText(Bundle.Toolbar_tagImageViewLabel()); categoryImageViewLabel.setText(Bundle.Toolbar_categoryImageViewLabel()); thumbnailSizeLabel.setText(Bundle.Toolbar_thumbnailSizeLabel()); groupByBox.setItems(FXCollections.observableList(DrawableAttribute.getGroupableAttrs())); groupByBox.getSelectionModel().select(DrawableAttribute.PATH); groupByBox.getSelectionModel().selectedItemProperty().addListener(queryInvalidationListener); groupByBox.disableProperty().bind(ImageGalleryController.getDefault().regroupDisabled()); groupByBox.setCellFactory(listView -> new AttributeListCell()); groupByBox.setButtonCell(new AttributeListCell()); sortChooser = new SortChooser<>(GroupSortBy.getValues()); sortChooser.comparatorProperty().addListener((observable, oldComparator, newComparator) -> { final boolean orderEnabled = newComparator == GroupSortBy.NONE || newComparator == GroupSortBy.PRIORITY; sortChooser.setSortOrderDisabled(orderEnabled); final SortChooser.ValueType valueType = newComparator == GroupSortBy.GROUP_BY_VALUE ? SortChooser.ValueType.LEXICOGRAPHIC : SortChooser.ValueType.NUMERIC; sortChooser.setValueType(valueType); queryInvalidationListener.invalidated(observable); }); sortChooser.sortOrderProperty().addListener(queryInvalidationListener); sortChooser.setComparator(GroupSortBy.PRIORITY); getItems().add(1, sortChooser); sortHelpImageView.setCursor(Cursor.HAND); sortHelpImageView.setOnMouseClicked(clicked -> { Text text = new Text(Bundle.Toolbar_sortHelp()); text.setWrappingWidth(480); //This is a hack to fix the layout. showPopoverHelp(sortHelpImageView, Bundle.Toolbar_sortHelpTitle(), sortHelpImageView.getImage(), text); }); } /** * * Static utility to to show a Popover with the given Node as owner. * * @param owner The owner of the Popover * @param headerText A short String that will be shown in the top-left * corner of the Popover. * @param headerImage An Image that will be shown at the top-right corner of * the Popover. * @param content The main content of the Popover, shown in the * bottom-center * */ private static void showPopoverHelp(final Node owner, final String headerText, final Image headerImage, final Node content) { Pane borderPane = new BorderPane(null, null, new ImageView(headerImage), content, new Label(headerText)); borderPane.setPadding(new Insets(10)); borderPane.setPrefWidth(500); PopOver popOver = new PopOver(borderPane); popOver.setDetachable(false); popOver.setArrowLocation(PopOver.ArrowLocation.TOP_CENTER); popOver.show(owner); } private void syncGroupControlsEnabledState(GroupViewState newViewState) { boolean noGroupSelected = newViewState == null ? true : newViewState.getGroup() == null; tagGroupMenuButton.setDisable(noGroupSelected); catGroupMenuButton.setDisable(noGroupSelected); } public void reset() { Platform.runLater(() -> { groupByBox.getSelectionModel().select(DrawableAttribute.PATH); sizeSlider.setValue(SIZE_SLIDER_DEFAULT); }); } public Toolbar(ImageGalleryController controller) { this.controller = controller; FXMLConstructor.construct(this, "Toolbar.fxml"); //NON-NLS } }